//Orion's Standard Library
//Orion Sky Lawlor, 11/3/1999
//NAME:		osl/vector3d.h
//
//DESCRIPTION:	C++ 3-Dimensional vector library
//
//This file provides various utility routines for easily
//manipulating 3-D vectors-- included are arithmetic,
//dot/cross product, magnitude and normalization terms. 
//Most routines are provided right in the header file (for inlining).
#pragma once

#include "OrionFlames.h"

#undef min//Microsoft Windows header file pollution.
#undef max

namespace OrionFlames
{
//Vector3d is a cartesian vector in 3-space-- an x, y, and z.
//For cross products, the space is assumed to be right-handed (x cross y = +z).
template <class real>
class Vector3dT
{
	typedef Vector3dT<real> vec;

public:
	Vector3dT() { }
	//Simple 1-value constructors.
	explicit Vector3dT(int init) { x = y = z = (real)init; }
	explicit Vector3dT(float init) { x = y = z = (real)init; }
	explicit Vector3dT(double init) { x = y = z = (real)init; }
	
	//3-value constructor.
	Vector3dT(const real Nx, const real Ny, const real Nz) { x = Nx; y = Ny; z = Nz; }
	//Real array constructor.
	Vector3dT(const real *arr) { x = arr[0]; y = arr[1]; z = arr[2]; }

	//Constructors from other types of Vector:
	Vector3dT(const Vector3dT<float> &src) { x = (real)src.x; y = (real)src.y; z = (real)src.z; }
	Vector3dT(const Vector3dT<double> &src) { x = (real)src.x; y = (real)src.y; z = (real)src.z; }
	Vector3dT(const Vector3dT<int> &src) { x = (real)src.x; y = (real)src.y; z = (real)src.z; }

	//Copy constructor & assignment operator are by default.
	
	//This lets you typecast a vector to a real array.
	operator real *() { return (real*)&x; }
	operator const real *() const { return (const real*)&x; }

	//Basic mathematical operators.
	int operator == (const vec &b) const { return (x == b.x) && (y == b.y) && (z == b.z); }
	int operator != (const vec &b) const { return (x != b.x) || (y != b.y) || (z != b.z); }
	vec operator + (const vec &b) const	 { return vec(x + b.x, y + b.y, z + b.z); }
	vec operator - (const vec &b) const  { return vec(x - b.x, y - b.y, z - b.z); }
	vec operator * (const real scale) const { return vec(x * scale, y * scale, z * scale); }
	vec operator / (const real &div) const { real scale = (real)1.0 / div; return vec(x * scale, y * scale, z * scale); }
	vec operator - () const { return vec(-x, -y, -z); }
	void operator += (const vec &b) {x += b.x; y += b.y; z += b.z; }
	void operator -= (const vec &b) {x -= b.x; y -= b.y; z -= b.z; }
	void operator *= (const real scale) { x *= scale; y *= scale; z *= scale; }
	void operator /= (const real div) { real scale = 1.0 / div; x *= scale; y *= scale; z *= scale; }

	//Vector-specific operations.
	//Return the square of the magnitude of this vector.
	real magSqr() const { return x * x + y * y + z * z; }
	
	//Return the magnitude (length) of this vector.
	real mag() const { return sqrt(magSqr()); }
	
	//Return the square of the distance to the vector b.
	real distSqr(const vec &b) const { return (x - b.x) * (x - b.x) + (y - b.y) * (y - b.y) + (z - b.z) * (z - b.z); }

	//Return the distance to the vector b.
	real dist(const vec &b) const { return sqrt(distSqr(b)); }
	
	//Return the dot product of this vector and b.
	real dot(const vec &b) const { return x * b.x + y * b.y + z * b.z; }
	
	//Return the cosine of the angle between this vector and b.
	real cosAng(const vec &b) const { return dot(b) / (mag() * b.mag()); }
	
	//Return the "direction" (unit vector) of this vector.
	vec dir() const { return (*this) / mag(); }
	
	//Return the right-handed cross product of this vector and b.
	vec cross(const vec &b) const { return vec(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); }

	//Make this vector have unit length.
	void normalize() { *this = this->dir(); }
	
	//Return the largest coordinate in this vector.
	real max() const
	{
		real big = x;

		if (big < y)
			big = y;

		if (big < z)
			big = z;

		return big;
	}

	//Make each of this vector's coordinates at least as big as the given vector's coordinates.
	void enlarge(const vec &by)
	{
		if (x < by.x)
			x = by.x;

		if (y < by.y)
			y = by.y;

		if (z < by.z)
			z = by.z;     
	}

	real x, y, z;
};

//Utility wrapper routines.
template<class real>
inline real dist(const Vector3dT<real> &a, const Vector3dT<real> &b) { return a.dist(b); }

template<class real>
inline real dot(const Vector3dT<real> &a, const Vector3dT<real> &b) { return a.dot(b); }

template<class real>
inline Vector3dT<real> cross(const Vector3dT<real> &a, const Vector3dT<real> &b) { return a.cross(b); }

typedef Vector3dT<int> Vector3i;
typedef Vector3dT<float> Vector3f;
typedef Vector3dT<double> Vector3d;

//Allow "3.0 * vec" to compile.
template <class scalar_t,class real>
inline Vector3dT<real> operator * (const scalar_t scale, const Vector3dT<real> &v) { return Vector3dT<real>((real)(v.x * scale), (real)(v.y * scale), (real)(v.z * scale)); }

//Describes how much of a bounding volume is visible.
enum boundview_t
{
	boundview_none = 0, //Object is entirely outside view volume.
	boundview_part = 1, //Object is partially inside view volume.
	boundview_all = 2   //Object is entirely inside view volume.
};
};
